/**
 * 阿里云OSS文件上传工具类
 * 提供文件上传、分片上传、批量上传、文件删除等功能
 * 支持进度回调、成功回调、错误回调
 * 
 * @author 开发者
 * @date 2024
 * @version 1.0.0
 */

// ==================== 依赖导入 ====================
import OSS from 'ali-oss'                          // 阿里云OSS SDK
import { OSSConfig, getOSSConfig } from './ossConfig'  // OSS配置相关

// ==================== 类型定义 ====================

/**
 * 上传进度信息接口
 * 用于回调函数中传递上传进度数据
 */
export interface UploadProgress {
  loaded: number    // 已上传字节数
  total: number     // 文件总字节数
  percent: number   // 上传百分比 (0-100)
}

/**
 * 上传结果接口
 * 上传成功后返回的文件信息
 */
export interface UploadResult {
  url: string       // 文件访问URL
  name: string      // 文件在OSS中的名称
  size: number      // 文件大小（字节）
}

/**
 * 上传选项接口
 * 配置上传过程中的回调函数
 */
export interface UploadOptions {
  onProgress?: (progress: UploadProgress) => void  // 进度回调函数
  onSuccess?: (result: UploadResult) => void       // 成功回调函数
  onError?: (error: Error) => void                 // 错误回调函数
}

/**
 * OSS上传器类
 * 封装阿里云OSS文件上传的各种操作
 * 采用单例模式，确保全局只有一个实例
 */
class OSSUploader {
  private client: OSS | null = null        // OSS客户端实例
  private config: OSSConfig | null = null  // OSS配置信息

  /**
   * 初始化OSS客户端
   * 从配置文件获取OSS配置信息并创建客户端实例
   * 
   * @returns Promise<void> 初始化完成
   * @throws Error 当配置获取失败或客户端创建失败时抛出错误
   */
  async init(): Promise<void> {
    try {
      // 获取OSS配置信息
      this.config = await getOSSConfig()
      
      // 创建OSS客户端实例
      this.client = new OSS({
        region: this.config.region,           // 地域
        accessKeyId: this.config.accessKeyId, // 访问密钥ID
        accessKeySecret: this.config.accessKeySecret, // 访问密钥Secret
        bucket: this.config.bucket,           // 存储桶名称
        endpoint: this.config.endpoint        // 访问域名
      })
      
      console.log('OSS客户端初始化成功')
    } catch (error) {
      console.error('OSS初始化失败:', error)
      throw new Error('OSS初始化失败')
    }
  }

  /**
   * 生成唯一文件名
   * 使用时间戳和随机字符串确保文件名唯一性
   * 文件存储在 medical-images/ 目录下
   * 
   * @param originalName 原始文件名
   * @returns 生成的唯一文件名
   */
  private generateFileName(originalName: string): string {
    const timestamp = Date.now()                                    // 当前时间戳
    const random = Math.random().toString(36).substring(2, 15)     // 随机字符串
    const extension = originalName.split('.').pop()                // 文件扩展名
    return `medical-images/${timestamp}-${random}.${extension}`    // 组合生成唯一文件名
  }

  /**
   * 检查文件大小
   * 验证文件大小是否超过限制，防止上传过大文件
   * 
   * @param file 要检查的文件
   * @param maxSize 最大文件大小（字节），默认100MB
   * @throws Error 当文件超过大小限制时抛出错误
   */
  private checkFileSize(file: File, maxSize: number = 100 * 1024 * 1024): void {
    if (file.size > maxSize) {
      throw new Error(`文件大小不能超过 ${maxSize / 1024 / 1024}MB`)
    }
  }

  /**
   * 普通文件上传
   * 适用于小文件（<10MB），使用put方法直接上传
   * 支持进度回调、成功回调和错误回调
   * 
   * @param file 要上传的文件
   * @param options 上传选项配置
   * @returns Promise<UploadResult> 上传结果
   * @throws Error 当上传失败时抛出错误
   */
  async uploadFile(
    file: File, 
    options: UploadOptions = {}
  ): Promise<UploadResult> {
    // 确保OSS客户端已初始化
    if (!this.client) {
      await this.init()
    }

    // 检查文件大小限制（默认100MB）
    this.checkFileSize(file)

    // 生成唯一文件名
    const fileName = this.generateFileName(file.name)
    
    try {
      // 执行文件上传
      const result = await this.client!.put(fileName, file, {
        progress: (p: number) => {
          // 进度回调：将小数进度转换为具体字节数
          if (options.onProgress) {
            options.onProgress({
              loaded: Math.round(p * file.size),  // 已上传字节数
              total: file.size,                   // 总字节数
              percent: Math.round(p * 100)        // 百分比
            })
          }
        }
      })

      // 构造上传结果
      const uploadResult: UploadResult = {
        url: result.url,      // 文件访问URL
        name: fileName,       // 文件名
        size: file.size       // 文件大小
      }

      // 触发成功回调
      if (options.onSuccess) {
        options.onSuccess(uploadResult)
      }

      return uploadResult
    } catch (error) {
      const err = error as Error
      // 触发错误回调
      if (options.onError) {
        options.onError(err)
      }
      throw err
    }
  }

  /**
   * 分片上传（适用于大文件）
   * 将大文件分割成多个小片段进行上传，提高上传成功率和速度
   * 适用于文件大小 > 10MB 的情况
   * 
   * @param file 要上传的大文件
   * @param options 上传选项配置
   * @returns Promise<UploadResult> 上传结果
   * @throws Error 当上传失败时抛出错误
   */
  async uploadLargeFile(
    file: File,
    options: UploadOptions = {}
  ): Promise<UploadResult> {
    // 确保OSS客户端已初始化
    if (!this.client) {
      await this.init()
    }

    // 检查文件大小限制（大文件限制500MB）
    this.checkFileSize(file, 500 * 1024 * 1024)

    // 生成唯一文件名
    const fileName = this.generateFileName(file.name)
    const chunkSize = 1024 * 1024 // 每个分片1MB

    try {
      // 开始分片上传
      const result = await this.client!.multipartUpload(fileName, file, {
        partSize: chunkSize,  // 设置分片大小
        progress: (p: number) => {
          // 进度回调
          if (options.onProgress) {
            options.onProgress({
              loaded: Math.round(p * file.size),  // 已上传字节数
              total: file.size,                   // 总字节数
              percent: Math.round(p * 100)        // 百分比
            })
          }
        }
      })

      // 构造上传结果
      const uploadResult: UploadResult = {
        url: result.res.requestUrls[0].split('?')[0], // 获取不带参数的URL
        name: fileName,       // 文件名
        size: file.size       // 文件大小
      }

      // 触发成功回调
      if (options.onSuccess) {
        options.onSuccess(uploadResult)
      }

      return uploadResult
    } catch (error) {
      const err = error as Error
      // 触发错误回调
      if (options.onError) {
        options.onError(err)
      }
      throw err
    }
  }
  /**
   * 智能上传
   * 根据文件大小自动选择合适的上传方式
   * 小文件使用普通上传，大文件使用分片上传
   * 
   * @param file 要上传的文件
   * @param options 上传选项配置
   * @returns Promise<UploadResult> 上传结果
   */
  async smartUpload(
    file: File,
    options: UploadOptions = {}
  ): Promise<UploadResult> {
    const largeFileThreshold = 10 * 1024 * 1024 // 10MB阈值

    // 根据文件大小选择上传方式
    if (file.size > largeFileThreshold) {
      return this.uploadLargeFile(file, options)  // 大文件使用分片上传
    } else {
      return this.uploadFile(file, options)       // 小文件使用普通上传
    }
  }

  /**
   * 批量上传
   * 同时上传多个文件，使用Promise.all并行处理
   * 每个文件都会触发相应的回调函数
   * 
   * @param files 要上传的文件数组
   * @param options 上传选项配置
   * @returns Promise<UploadResult[]> 所有文件的上传结果数组
   */
  async uploadMultipleFiles(
    files: File[],
    options: UploadOptions = {}
  ): Promise<UploadResult[]> {
    // 为每个文件创建上传Promise
    const uploadPromises = files.map((file) => {
      return this.smartUpload(file, {
        onProgress: (progress) => {
          // 批量上传进度回调
          if (options.onProgress) {
            options.onProgress({
              loaded: progress.loaded,
              total: progress.total,
              percent: progress.percent
            })
          }
        },
        onSuccess: (result) => {
          // 单个文件上传成功回调
          if (options.onSuccess) {
            options.onSuccess(result)
          }
        },
        onError: (error) => {
          // 单个文件上传失败回调
          if (options.onError) {
            options.onError(error)
          }
        }
      })
    })
    // 并行执行所有上传任务
    return Promise.all(uploadPromises)
  }
  /**
   * 删除文件
   * 从OSS存储桶中删除指定文件
   * 
   * @param fileName 要删除的文件名（包含路径）
   * @returns Promise<void> 删除完成
   * @throws Error 当删除失败时抛出错误
   */
  async deleteFile(fileName: string): Promise<void> {
    // 确保OSS客户端已初始化
    if (!this.client) {
      await this.init()
    }

    try {
      // 执行文件删除
      await this.client!.delete(fileName)
      console.log(`文件删除成功: ${fileName}`)
    } catch (error) {
      console.error('删除文件失败:', error)
      throw new Error('删除文件失败')
    }
  }
}

// ==================== 单例实例和便捷方法 ====================

/**
 * 创建OSS上传器单例实例
 * 确保全局只有一个上传器实例，避免重复初始化
 */
export const ossUploader = new OSSUploader()

/**
 * 便捷的文件上传方法
 * 使用智能上传策略，根据文件大小自动选择上传方式
 * 
 * @param file 要上传的文件
 * @param options 上传选项配置
 * @returns Promise<UploadResult> 上传结果
 * 
 * @example
 * ```typescript
 * const result = await uploadFile(file, {
 *   onProgress: (progress) => console.log(`上传进度: ${progress.percent}%`),
 *   onSuccess: (result) => console.log('上传成功:', result.url),
 *   onError: (error) => console.error('上传失败:', error)
 * });
 * ```
 */
export const uploadFile = (file: File, options?: UploadOptions) => 
  ossUploader.smartUpload(file, options)

/**
 * 便捷的批量文件上传方法
 * 并行上传多个文件
 * 
 * @param files 要上传的文件数组
 * @param options 上传选项配置
 * @returns Promise<UploadResult[]> 所有文件的上传结果
 * 
 * @example
 * ```typescript
 * const results = await uploadMultipleFiles(files, {
 *   onProgress: (progress) => console.log(`批量上传进度: ${progress.percent}%`),
 *   onSuccess: (result) => console.log('文件上传成功:', result.url),
 *   onError: (error) => console.error('文件上传失败:', error)
 * });
 * ```
 */
export const uploadMultipleFiles = (files: File[], options?: UploadOptions) => 
  ossUploader.uploadMultipleFiles(files, options)

/**
 * 便捷的文件删除方法
 * 删除OSS存储桶中的指定文件
 * 
 * @param fileName 要删除的文件名（包含路径）
 * @returns Promise<void> 删除完成
 * 
 * @example
 * ```typescript
 * await deleteFile('medical-images/1234567890-abc123.jpg');
 * ```
 */
export const deleteFile = (fileName: string) => 
  ossUploader.deleteFile(fileName)